home *** CD-ROM | disk | FTP | other *** search
- /*
- * preempt.c
- *
- *
- * Preemption interrupt routines.
- *
- * Preemption works by having a single designated process(or) take a VTALRM
- * every Scheduler::sc_quantum milliseconds. The designated process
- * takes a snapshot of the readyq, estimating how many ready threads
- * there are. For each processor, if that processor is running a
- * preemptable thread (scheduler threads are non-preemptable), and
- * the thread has been running for longer than the quantum, the processor
- * on which the thread is running is handed a a SIGUSR1, to force
- * a resched.
- *
- * In the resched interrupt handler (or from the VTALRM handler if
- * the designated alrm handler is running on a processor which needs
- * to be preempted), we diddle with the return sigcontext so that on
- * return from the signal handler, we will be running a preface to the
- * actual thread switch routine, as though it were called directly
- * by the user thread. This makes context switching always appear
- * synchronous from the standpoint of the scheduler.
- *
- *
- * The signal handling code here is (unfortunately) machine dependent and
- * will need to be rewritten somewhat to run under 4.3 or the vax.
- *
- * This code needs to be made part of a general sigobject that interfaces
- * PRESTO code to asynchronous events.
- */
-
-
- #define _PREEMPT_C
-
- #include <sys/types.h>
- #include <signal.h>
- extern int sigstack(struct sigstack *ss, struct sigstack *oss);
- #include <osfcn.h>
- #include "presto.h"
-
- //
- // On the sequent, each process has a private interrupts_enabled
- // variable. On other machines, interrupts_enabled is implemented
- // as a field in each process object. If interrupts are disabled,
- // then thisthread is, by definition, not preemptable. A processor
- // could be running with interrupts enabled, but thisthread could
- // not be allowing preemption. See functions for enabling and
- // disabling interrupts at the bottom of this file.
- //
-
- #ifdef sequent
- private_t int interrupts_enabled = 1;
- #endif sequent
- #ifdef sun
- # ifndef THREAD_HAS_INTERRUPTIBLE_FIELD
- private_t int interrupts_enabled = 1;
- # endif
- #endif
-
- int disable_interrupts();
- void enable_interrupts();
- int interrupts_are_enabled();
-
- int preemption_enabled = 0; // preemption ticker is running
-
- #ifdef PREEMPT
-
- static private_t int osigmask;
-
- void sigpreempt_init();
- int sigpreempt_notify(int sig, int code, struct sigcontext *scp);
- int sigpreempt_alrm(int sig, int code, struct sigcontext *scp);
- void sigpreempt_unblock();
-
- //
- // signal reentry point
- //
- void preempt_reentry();
-
- shared_t int numalarms = 0; // damn the concurrency
- shared_t int numnotifies = 0;
- shared_t int numfalsehits = 0;
-
- #define SIGSTACKSIZ 4096
- #define SIGPREEMPT_NOTIFY SIGUSR1
- #define SIGPREEMPT_ALRM SIGVTALRM
-
- //
- // take the two preempt signals on the signal stack
- //
- static struct sigvec svec_notify = {
- sigpreempt_notify, sigmask(SIGPREEMPT_NOTIFY), 1};
-
- static struct sigvec svec_alrm ={
- sigpreempt_alrm, sigmask(SIGPREEMPT_ALRM), 1};
-
- static private_t int sstack[SIGSTACKSIZ];
- static struct sigstack signalstack = { (caddr_t)&sstack[SIGSTACKSIZ-1], 0 };
-
-
- // force kernel sigvec to be called
- #ifdef sequent
- # define SIGVEC_OS _sigvec
- #else
- # define SIGVEC_OS sigvec
- #endif
-
- extern int SIGVEC_OS(int, struct sigvec*, struct sigvec*);
-
- void
- sigpreempt_init()
- {
- extern int sigblock(int);
-
- struct sigstack oss;
-
- // Check if we are already running on an alternate stack
- if (::sigstack((struct sigstack*)0, &oss) < 0) {
- perror("sigstack1");
- fatalerror();
- }
-
- if (oss.ss_sp == 0) { // must set our own
- if (::sigstack(&signalstack, (struct sigstack*)0) < 0) {
- perror("sigstack2");
- fatalerror();
- }
- }
-
- if (SIGVEC_OS(SIGPREEMPT_ALRM, &svec_alrm, 0) < 0) {
- perror("sigvecusr1");
- fatalerror();
- }
-
- if (SIGVEC_OS(SIGPREEMPT_NOTIFY, &svec_notify, 0) < 0) {
- perror("sigvecvtalrm");
- fatalerror();
- }
-
- osigmask = sigblock(0); // get original signal mask
- return;
- }
-
-
- void
- sigpreempt_stopclock()
- {
- extern int setitimer(int, itimerval*, itimerval*);
- struct itimerval it;
-
- disable_interrupts();
- preemption_enabled = 0;
-
- sigblock(sigmask(SIGPREEMPT_NOTIFY)|sigmask(SIGSEGV));
- it.it_interval.tv_sec = 0;
- it.it_interval.tv_usec = 0;
- it.it_value.tv_sec = 0;
- it.it_value.tv_usec = 0;
- setitimer(ITIMER_VIRTUAL, &it, (struct itimerval*)0);
- cerr << "Numalarms " << numalarms << "\tNumnotifies " << numnotifies << "\n";
- }
-
-
- //
- // this should only run in the parent master scheduler
- //
- void
- sigpreempt_beginclock(struct timeval *quantum)
- {
- struct itimerval it;
-
- preemption_enabled = 1;
- if (quantum->tv_sec == 0 && quantum->tv_usec < 100000)
- quantum->tv_usec = 100000; // 100ms minimum quantum
- it.it_interval = *quantum;
- it.it_value = *quantum;
- if (setitimer(ITIMER_VIRTUAL, &it, (struct itimerval*)0) < 0) {
- perror("setitimer");
- fatalerror();
- }
- sigpreempt_unblock();
- enable_interrupts();
- }
-
-
-
- void
- sigpreempt_unblock()
- {
- extern int sigsetmask(int);
- sigsetmask(osigmask);
- }
-
-
-
-
- //
- // handler for taking care of preemption when signaled. We can be called
- // synchronously: if the vtalrm handler process needs to be preempted
- // when done handling the alarm
- //
- // asynchronously:
- // if the process on which we are running needs to get
- // preempted as decided by the vtalrm handler.
- //
- // We block the offending signal for after we return until after
- // the main preemption code has done its thing.
-
-
-
- int
- sigpreempt_notify( int sig, int code, struct sigcontext *scp)
- {
- int *sp = (int*)scp->sc_sp;
-
- if (!interrupts_are_enabled()) {
- code=code; // make compiler happy
- return 0;
- }
-
- #ifdef ns32000
- sp[0] = scp->sc_modpsr;
- #endif
- #ifdef i386
- sp[0] = scp->sc_flags;
- #endif
- //#if (sequent || sun)
- #ifdef sequent
- sp[1] = scp->sc_pc;
- scp->sc_sp -= 2 * sizeof(int);
- #endif (sequent || sun)
-
- #ifdef vax
- //
- // What we *really* want to do is push the interrupted pc on the
- // stack and fake a jsb to preempt_reentry, but Ultrix won't
- // let us change the stack pointer (sigh). Instead we save
- // the interrupted pc in the thread object, set our sigcontext
- // pc to preempt_reentry and fix things up to look like an
- // interrupt occurred once we get there.
- //
- thisthread->setpc(scp->sc_pc);
- #endif vax
- #ifdef sun
- thisthread->setpc(scp->sc_pc);
- #endif sun
-
- scp->sc_pc = (int)preempt_reentry;
-
- //
- // block the incoming signal until we are through preempting
- //
- osigmask = scp->sc_mask;
- scp->sc_mask |= sigmask(sig);
-
- numnotifies++;
-
- return 0;
- }
-
- //
- //
- // VTALRM handler for process designated to control preemption.
- // We are a friend of the scheduler
- //
- int
- sigpreempt_alrm( int sig, int code, struct sigcontext *scp )
- {
- register *sp = (int*)scp->sc_sp;
- Process *p;
- Thread *t;
- int numtopreempt;
- int i;
-
-
- numtopreempt = sched->readyqlen();
-
- // for fairness, we should cycle through modulo the number
- // of processors, not always start at the beginning.
- //
- numalarms++;
-
- double q = ((double)sched->quantum()) / 1000.0;
- for (i = 0; numtopreempt && i < sched->sc_p_activeschedulers; i++) {
- p = sched->sc_p_procs[i];
-
- // slight race condition... we could get the running
- // thread just as it is returning to the scheduler. We
- // only can consider a NOTIFY interrupt as being
- // a hint that we should really preempt.
-
- t = p->runningthread();
- if (t && t->canpreempt()) {
- // DESIGNATED PROC MUST BE PREEMPTED
- // Save ourselves from having to handle yet another
- // signal.
- if (p == thisproc) {
- (void)sigpreempt_notify(sig, code, scp);
- } else {
- // just signal the other proc
- kill(p->pid(), SIGPREEMPT_NOTIFY);
- }
- numtopreempt--;
- }
- }
- return 0;
- }
-
-
-
- //
- //
- // Signal handler reentry point. From the standpoint of the system,
- // it looks as though the currently executing thread falls into this
- // routine.
- //
- // preempt_reentry_() is a never-called label.
- //
-
- #ifdef vax
- int _hack;
- #endif vax
- #ifdef sun
- int _hack;
- #endif sun
-
- void
- preempt_reentry_()
- {
- void preempt_preempt();
- asm(" .globl _preempt_reentry");
- asm("_preempt_reentry:");
-
- // must be sure to run off own frame in preempt_preempt. At this
- // point, we are on the frame of the interrupted routine.
-
- #ifdef vax
- //
- // The following hack is compiler-dependent and dangerous.
- // We are assuming that getpc uses no registers besides r0.
- // Also, this won't work on a multiprocessor because we are
- // using a global (_hack) as a temporary for the interrupted
- // pc. This is a "temporary" uniprocessor vax hack. The code
- // should be changed to use a faked jsb/rsb when the Ultrix
- // bug alluded to in sigpreempt_notify is fixed.
- //
- // Here's the plan:
- // 1. Push the psl and r0 on the stack.
- // 2. Get the interrupted pc in _hack, push it on the stack.
- // 3. Call preempt_preempt().
- // 4. Sleazily restore r0.
- // 5. Pop the pushed pc, put it where r0 was on the stack.
- // 6. Restore the pc and psl with an REI.
- //
- asm("movpsl -(sp)");
- asm("pushl r0");
- _hack = thisthread->getpc();
- asm("pushl __hack");
- #endif vax
- #ifdef sun
- //
- // We are doing a similar thing on the sun, only since I don't
- // know what to do with the status register (can't find the
- // *!$! mnemonic), I just ignore it.
- //
- asm("movl a0, sp@-");
- _hack = thisthread->getpc();
- asm("movl __hack, sp@-");
- #endif sun
-
- preempt_preempt();
-
- // simulate a rti
- #ifdef ns32000
- asm("lprw upsr,2(sp)");
- asm("adjspb -4");
- asm("ret 0");
- #endif
- #ifdef i386
- asm("popfl");
- asm("ret");
- #endif
- #ifdef sun
- //
- // Stack should look like: sp-> PC
- // a0
- //
- // We are doing this: PC
- // sp-> PC
- //
- asm("movl sp@(4), a0");
- asm("movl sp@, sp@(4)");
- asm("addql #4, sp ");
- asm("rts");
- #endif sun
-
- //
- // Vax stack looks like: sp-> PC
- // r0
- // PSL
- //
- // What we are doing is this:
- // PC
- // sp-> PC
- // PSL
- //
- #ifdef vax
- asm("movl 4(sp), r0");
- asm("movl (sp), 4(sp)");
- asm("addl2 $4, sp");
- asm("rei");
- #endif
-
- }
-
-
- //
- // preempt_preempt()
- //
- // we are running as though we were called synchronously
- // by the currently executing thread. Place the current
- // thread on the ready queue and swtch back to the scheduler
- // thread. For a tiny instant, the thread we place on the
- // readyqueue will remain locked until the scheduler thread
- // unlocks it. This will only be a problem if some other
- // processor grabs it off the ready queue too soon. But,
- // the fact that we are preempting means that the readyqueue
- // is long, so it is not likely that this will happen. Even if
- // it does, it only means that the scheduler thread which pulls
- // it off will briefly spin.
- //
- void
- preempt_preempt()
- {
- int checkpreempt_request();
-
- //
- // save scratch registers
- //
- #ifdef mc68020
- {
- asm("movl a0, sp@- ");
- asm("movl a1, sp@- ");
- asm("movl a2, sp@- ");
- }
- #endif mc68020
- #ifdef ns32000
- {
- asm("movd r0, tos");
- asm("movd r1, tos");
- asm("movd r2, tos");
- }
- #endif
- #ifdef i386
- {
- asm("pushl %eax");
- asm("pushl %ecx");
- asm("pushl %edx");
- }
- #endif
- #ifdef vax
- asm("movl r0, -(sp)");
- asm("movl r1, -(sp)");
- asm("movl r2, -(sp)");
- #endif
-
- //
- // signal callout mechanism belongs here
- //
- if (checkpreempt_request()) {
- sched->resume(thisthread);
- sigpreempt_unblock();
- if (thisthread->inspinlock())
- error("Preempted thread holds a lock!");
- thisthread->swtch();
- } else {
- numfalsehits++;
- sigpreempt_unblock();
- }
-
- #ifdef mc68020
- {
- asm("movl sp@+, a2 ");
- asm("movl sp@+, a1 ");
- asm("movl sp@+, a0 ");
- }
- #endif mc68020
- #ifdef ns32000
- {
- // replace scratch registers
- asm("movd tos, r2");
- asm("movd tos, r1");
- asm("movd tos, r0");
-
- }
- #endif
- #ifdef i386
- {
- asm("popl %edx");
- asm("popl %ecx");
- asm("popl %eax");
- }
- #endif
- #ifdef vax
- asm("movl (sp)+, r2");
- asm("movl (sp)+, r1");
- asm("movl (sp)+, r0");
- #endif vax
- }
-
- //
- // Verify that the currently executing thread really wants to be
- // preempted.
- //
-
- static int
- checkpreempt_request()
- {
- if (thisthread->canpreempt())
- return 1;
- #ifndef PREEMPT_DEBUG
- else
- return 0;
- #else
- //
- // try to get a little more specific about why it can't be
- //
- if ( !thisthread->ispreemptable()) {
- cerr << thisthread->flags()
- << "Bad Preempt: Thread is not preemptaable\n";
- return 0;
- } else if (thisthread->flags()&TF_SCHEDULER) {
- cerr << "Bad Preempt: scheduler is running\n";
- return 0;
- }
- #endif
- }
-
- #endif PREEMPT
-
- //
- // These functions allow presto kernel code to mark process objects as
- // non-interruptible. This is used by the memory allocation code when
- // acquiring and releasing the malloc spinlock, and is also used to
- // disable preemption ticks during a context switch. Note that the
- // process is not interruptible even when it is spinning waiting for
- // the malloc lock. This is also a problem for Spinlock objects. XXX
- //
- // On the sequent a private type is available, and using it for
- // interrupts_enabled saves a few instructions on context switches.
- // On other machines (vax), each process object has a p_interruptible
- // field and public member functions for enabling and disabling
- // interrupts on that process object.
- //
-
-
- #ifdef sun
- # ifdef THREAD_HAS_INTERRUPTIBLE_FIELD
- int interrupts_are_enabled() {
- if (preemption_enabled) {
- return thisproc->interruptible();
- } else
- return 0;
- }
-
- int disable_interrupts() {
- if (preemption_enabled) {
- return thisproc->disable_interrupts();
- } else
- return 0;
- }
-
- void enable_interrupts() {
- if (preemption_enabled)
- thisproc->enable_interrupts();
- }
- # else
- int interrupts_are_enabled() { return interrupts_enabled; }
-
- int disable_interrupts() {
- if (interrupts_enabled) {
- interrupts_enabled = 0;
- return 1;
- } else
- return 0;
- }
-
- void enable_interrupts() { interrupts_enabled = 1; }
- # endif THREAD_HAS_INTERRUPTIBLE_FIELD
- #endif sun
- #ifdef sequent
-
- int
- interrupts_are_enabled()
- {
- return interrupts_enabled;
- }
-
- int
- disable_interrupts()
- {
- if (interrupts_enabled) {
- interrupts_enabled = 0;
- return 1;
-
- } else
- return 0;
-
- }
-
- void
- enable_interrupts()
- {
- interrupts_enabled = 1;
- }
-
- #endif sequent
- #ifdef vax
- int
- interrupts_are_enabled()
- {
- if (preemption_enabled) {
- return thisproc->interruptible();
- } else
- return 0;
- }
-
- int
- disable_interrupts()
- {
- if (preemption_enabled) {
- return thisproc->disable_interrupts();
- } else
- return 0;
- }
-
- void
- enable_interrupts()
- {
- if (preemption_enabled)
- thisproc->enable_interrupts();
- }
-
- #endif vax
-